#pragma once
/*
消息格式化类:
    formatter:格式化
    Item:项（子项）

    1.抽象一个格式化基类
    2.基于基类，派生出不同的格式化子项子类
    子项：
    {
        日志输出时间
        日志等级
        源文件名称
        源代码行号
        线程ID
        日志主体消息
        日志器名称
        换行
        制表符
        其他
    }
    这样就可以在父类中定义父类指针的数组，指向不同的格式化子类对象（多态！）
*/

#include "util.hpp"
#include "message.hpp"
#include "level.hpp"
#include <memory>
#include <vector>
#include <tuple>
#include <sstream>

namespace hsl_log
{
    // 抽象一个"格式化项"基类
    class FormatItem
    {
    public:
        virtual ~FormatItem() {}
        // 等价于typedef std::shared_ptr<FormatItem> ptr;
        using ptr = shared_ptr<FormatItem>; // 别名
        virtual void format(ostream &out, LogMsg &msg) = 0;
    };

    /*
    基于基类，派生出不同的格式化子项子类
    子项：
    {
        日志输出时间
        日志等级
        源文件名称
        源代码行号
        线程ID
        日志主体消息
        日志器名称
        换行
        制表符
        其他
    }
    */
    // 消息主体
    class MsgFormatItem : public FormatItem
    {
    public:
        void format(ostream &out, LogMsg &msg) override
        {
            out << msg._payload;
        }
    };
    // 等级
    class LevelFormatItem : public FormatItem
    {
    public:
        void format(ostream &out, LogMsg &msg) override
        {
            out << LogLevel::toString(msg._level);
        }
    };
    // 时间
    class TimeFormatItem : public FormatItem
    {
    public:
        TimeFormatItem(const string &fmt = "%H:%M:%S")
            : _time_fmt(fmt)
        {
            // if (fmt.empty()) _time_fmt = "%H:%M:%S";
        }
        /*
        void format(ostream &out, LogMsg &msg) override
        {
            time_t t = msg._ctime;
            struct tm lt;
            localtime_r(&t, &lt);
            char tmp[128];
            strftime(tmp, 127, _time_fmt.c_str(), &lt);
            out << tmp;
        }
        */
        void format(ostream &out, LogMsg &msg) override
        {
            string tmp = TimeStampExLocalTime();
            out << tmp.c_str();
        }
        static std::string TimeStampExLocalTime()
        {
            time_t currtime = time(nullptr);
            struct tm *curr = localtime(&currtime);
            char time_buffer[128];
            snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",
                     curr->tm_year + 1900, curr->tm_mon + 1, curr->tm_mday,
                     curr->tm_hour, curr->tm_min, curr->tm_sec);
            return time_buffer;
        }

    private:
        string _time_fmt; //%H:%M:%S
    };

    // 行号
    class LineFormatItem : public FormatItem
    {
    public:
        void format(ostream &out, LogMsg &msg) override
        {
            out << msg._line;
        }
    };
    // 源文件名
    class FileFormatItem : public FormatItem
    {
    public:
        void format(ostream &out, LogMsg &msg) override
        {
            out << msg._file;
        }
    };
    // 线程ID
    class ThreadFormatItem : public FormatItem
    {
    public:
        void format(ostream &out, LogMsg &msg) override
        {
            out << msg._tid;
        }
    };
    // 日志器名称
    class LoggerFormatItem : public FormatItem
    {
    public:
        void format(ostream &out, LogMsg &msg) override
        {
            out << msg._name;
        }
    };
    // 制表符
    class TabFormatItem : public FormatItem
    {
    public:
        void format(ostream &out, LogMsg &msg) override
        {
            out << '\t';
        }
    };
    // 换行符
    class NLineFormatItem : public FormatItem
    {
    public:
        void format(ostream &out, LogMsg &msg) override
        {
            out << '\n';
        }
    };

    // 其他
    class OtherFormatItem : public FormatItem
    {
    public:
        OtherFormatItem(const string &str) : _str(str) {}

        void format(ostream &out, LogMsg &msg) override
        {
            out << _str;
        }

    private:
        string _str;
    };

    // 格式化类（主模块）
    /*
        %d 日期
        %T 缩进
        %t 线程id
        %p 日志级别
        %c 日志器名称
        %f 文件名
        %l 行号
        %m 日志消息
        %n 换行
    */
    class Formatter
    {
    public:
        using ptr = shared_ptr<Formatter>;
        Formatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%p][%c][%f:%l]%T%m%n") : _pattern(pattern)
        {
            assert(parsePattern());
        }
        // 格式化到标准输出
        void format(ostream &out, LogMsg &msg)
        {
            for (auto &items : _items)
            {
                items->format(out, msg);
            }
        }
        // 能获取到格式化后的字符串
        string format(LogMsg &msg)
        {
            stringstream ss;
            format(ss, msg);
            return ss.str();
        }

    private:
        // 解析字符串
        bool parsePattern()
        {
            // 1.对格式化规则进行解析
            vector<pair<string, string>> fmt_order;//存放key，val,方便最后存进成员变量_items里
            size_t pos = 0; //

            string key, val; // 分别存放  %x中的x and 其他字符(原始字符)

            while (pos < _pattern.size())
            {
                // 1.1处理字符串--判断是否为 %，否则就是其他字符
                if (_pattern[pos] != '%')
                {
                    val.push_back(_pattern[pos++]);
                    continue;
                }

                // 1.2到这说明pos位置就是%，还要判断一下%%的情况
                if (pos + 1 < _pattern.size() && _pattern[pos + 1] == '%')
                {
                    val.push_back('%');
                    pos += 2;
                    continue;
                }
                if (val.empty() == false)
                {
                    fmt_order.push_back(make_pair("", val));
                    val.clear();
                }

                // 1.3到这说明一定是格式化字符（%X）
                // pos位置就是%,pos+1位置就是我们要的字符
                pos += 1;
                if (pos == _pattern.size())
                {
                    cout << "%之后没有对应的格式化字符" << endl;
                    return false;
                }

                key = _pattern[pos];
                pos += 1;

                // 1.4这时pos指向格式化字符的下一个位置
                if (pos < _pattern.size() && _pattern[pos] == '{') // 如果是{
                {
                    pos += 1; // 指向子规则的起始位置：%
                    while (pos < _pattern.size() && _pattern[pos] != '}')
                    {
                        val.push_back(_pattern[pos++]);
                    }
                    if (pos == _pattern.size()) // 没有找到'}'
                    {
                        cout << "子规则{}匹配出错！\n";
                        return false;
                    }
                    pos += 1; // 这时候pos在‘}’位置，+1到
                }
                fmt_order.push_back(make_pair(key, val));
                key.clear();
                val.clear();
            }
            // 2.根据解析得到的数据格式化子项数组成员
            for (auto &it : fmt_order)
            {
                _items.push_back(createItem(it.first, it.second));
            }
            return true;
        }

        // 根据不同的格式化字符创建不同的格式化子项    %x           //其他字符(原始字符)
        FormatItem ::ptr createItem(const string &key, const string &val)
        {
            if (key == "d")
                return make_shared<TimeFormatItem>(val);
            if (key == "T")
                return make_shared<TabFormatItem>();
            if (key == "t")
                return make_shared<ThreadFormatItem>();
            if (key == "p")
                return make_shared<LevelFormatItem>();
            if (key == "c")
                return make_shared<LoggerFormatItem>();
            if (key == "f")
                return make_shared<FileFormatItem>();
            if (key == "l")
                return make_shared<LineFormatItem>();
            if (key == "m")
                return make_shared<MsgFormatItem>();
            if (key == "n")
                return make_shared<NLineFormatItem>();
            if (key == "")
                return make_shared<OtherFormatItem>(val);
            cout << "没有对应的格式化字符:%" << key << endl;
            abort();
        }

    private:
        string _pattern;                    // 符合格式化规则的字符串
        vector<FileFormatItem::ptr> _items; // 定义存放格式化对象的数组
    };
}
